/*
 *Copyright © 2018 anji-plus
 *安吉加加信息技术有限公司
 *http://www.anji-plus.com
 *All rights reserved.
 */
package captcha.util;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.Expiry;
import org.checkerframework.checker.index.qual.NonNegative;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.*;

/**
 * @program: 极度真实还原大麦网高并发实战项目。 添加 阿星不是程序员 微信，添加时备注 大麦 来获取项目的完整资料 
 * @description: CacheUtil
 * @author: 阿星不是程序员
 **/
public final class CacheUtil {
    private static final Logger logger = LoggerFactory.getLogger(CacheUtil.class);

    private static final Map<String, Object> CACHE_MAP = new ConcurrentHashMap<String, Object>();

    /**
     * 缓存最大个数
     */
    private static Integer CACHE_MAX_NUMBER = 1000;

    private static final Integer TWO = 2;

    private  static Cache<String, String> CACHE;

    private  static CacheExpiry CACHE_EXPIRY ;

    private static long RANDOM_DELETE_NUM = 10;

   static class CacheExpiry implements Expiry<String, String> {
       private final Cache<String,Integer> expiryCache =   Caffeine.newBuilder()
               .build();
        private  final Map<String,Integer> cacheExpireMap = new ConcurrentHashMap<>();

        public boolean set(String key,Integer expiresInSeconds){
            return cacheExpireMap.putIfAbsent(key,expiresInSeconds) == null;
        }
        public boolean remove(String key){
            return cacheExpireMap.remove(key) != null;
        }

        @Override
        public long expireAfterCreate(String key, String value, long currentTime) {
            long expire = cacheExpireMap.getOrDefault(key,10);
            return  TimeUnit.SECONDS.toNanos(expire);
        }

        @Override
        public long expireAfterUpdate(String key, String value, long currentTime, long currentDuration) {
            //快速走过
             if(currentDuration > TimeUnit.SECONDS.toNanos(10)){
                 return currentDuration;
             }else if (currentDuration < TimeUnit.SECONDS.toNanos(1)){
                 return currentDuration ;
             }
             else {
                 return currentDuration + currentDuration / 2;
             }
        }

       @Override
       public long expireAfterRead(String s, String o, long l, @NonNegative long l1) {
           return l1;
       }

   }

    /**
     * 初始化
     * @param cacheMaxNumber 缓存最大个数
     * @param interval 定时任务 秒执行清除过期缓存
     */
    public static void init(Integer cacheMaxNumber,long interval ,TimeUnit timeUnit,long  randomDeleteNum) {
        if( randomDeleteNum > RANDOM_DELETE_NUM){
            RANDOM_DELETE_NUM = randomDeleteNum;
        }
        CACHE_MAX_NUMBER = cacheMaxNumber;
        CACHE_EXPIRY = new CacheExpiry();
        CACHE = Caffeine.newBuilder()
                // 写入后3秒过期
                .expireAfter(CACHE_EXPIRY)
                // 最大缓存100个条目
                .maximumSize(CACHE_MAX_NUMBER)
                .build();


        if (interval > 0L) {

            scheduledExecutor = Executors.newScheduledThreadPool(1);

            scheduledExecutor.scheduleAtFixedRate(CacheUtil::randomDelete,10,interval,timeUnit);

            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
                @Override
                public void run() {
                    if(Objects.nonNull(scheduledExecutor)){
                        clear();
                        scheduledExecutor.shutdownNow();
                    }
                }
            }));

        }
//
////        CACHE_MAX_NUMBER = cacheMaxNumber;
//        if (second > 0L) {
//            scheduledExecutor = new ScheduledThreadPoolExecutor(1, new ThreadFactory() {
//				@Override
//				public Thread newThread(Runnable r) {
//					return new Thread(r,"thd-captcha-cache-clean");
//				}
//			},new ThreadPoolExecutor.CallerRunsPolicy());
//            scheduledExecutor.scheduleAtFixedRate(new Runnable() {
//				@Override
//				public void run() {
//					refresh();
//				}
//			},10,second,TimeUnit.SECONDS);
//
//            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
//                @Override
//                public void run() {
//                    if(Objects.nonNull(scheduledExecutor)){
//                        clear();
//                        scheduledExecutor.shutdownNow();
//                    }
//                }
//            }));
//        }
    }

    private static ScheduledExecutorService scheduledExecutor;

    /**
     * 缓存刷新,清除过期数据
     */
    public static void randomDelete(){

        ThreadLocalRandom random = ThreadLocalRandom.current();
        int size = CACHE_MAP.size();
        if(size <= RANDOM_DELETE_NUM){
            return;
        }
        Set<String> strings = CACHE_MAP.keySet();
        List<String> list = strings.stream().toList();
        for (int i = 0; i < RANDOM_DELETE_NUM; i++) {
            int randomNum = random.nextInt(0,size);
            String key = list.get(randomNum);
            if(CACHE.getIfPresent(key) != null){
                CACHE_EXPIRY.remove(key);
            }
        }

    }


    public static void set(String key, String value, long expiresInSeconds){

        CACHE_EXPIRY.set(key,(int)expiresInSeconds);
        CACHE.put(key,value);

//        //设置阈值，达到即clear缓存
//        if (CACHE_MAP.size() > CACHE_MAX_NUMBER * TWO) {
//            logger.info("CACHE_MAP达到阈值，clear map");
//            clear();
//        }
//        CACHE_MAP.put(key, value);
//        if(expiresInSeconds >0) {
//            //缓存失效时间
//			CACHE_MAP.put(key + "_HoldTime", System.currentTimeMillis() + expiresInSeconds * 1000);
//		}
    }

    public static void delete(String key){
        CACHE.invalidate(key);
        CACHE_EXPIRY.remove(key);
//        CACHE_MAP.remove(key);
//        CACHE_MAP.remove(key + "_HoldTime");
    }

    public static boolean exists(String key){
        return CACHE.getIfPresent(key) != null;
//        Long cacheHoldTime = (Long) CACHE_MAP.get(key + "_HoldTime");
//        if (cacheHoldTime == null || cacheHoldTime == 0L) {
//            return false;
//        }
//        if (cacheHoldTime < System.currentTimeMillis()) {
//            delete(key);
//            return false;
//        }
//        return true;
    }


    public static String get(String key){

        return CACHE.getIfPresent(key);
//        if (exists(key)) {
//            return (String)CACHE_MAP.get(key);
//        }
//        return null;
    }

    /**
     * 删除所有缓存
     */
    public static void clear() {
         CACHE.invalidateAll();
       logger.debug("have clean all key !");
//        CACHE_MAP.clear();
    }
}
